﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace Percolation.Gui
{
    public static class Extensions
    {
        public static T DeepCopy<T>(this T obj)
        {
            using (var stream = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, obj);
                stream.Position = 0;
                return (T) formatter.Deserialize(stream);
            }
        }
    }

    [Serializable]
    public sealed class NetPackSettings
    {
        public NetTypeEnum NetType { get; set; }

        public int Width { get; set; }
        public int Height { get; set; }

        public String GetDescription()
        {
            String resDescription = NetType.GetString() + " net pack";
            if (NetType == NetTypeEnum.Random)
                resDescription += " (x" + Count.ToString() + "; " + Probability0 + ")";
            else if (NetType == NetTypeEnum.VerGradient || NetType == NetTypeEnum.HorGradient)
                resDescription += " (x" + Count.ToString() + "; " + Probability0 + "‒" + Probability1 + ")";

            return resDescription;
        }

        public String GetShortDescription()
        {
            String resDescription = "";

            switch (NetType)
            {
                case NetTypeEnum.Random:
                    resDescription += "Rnd"; break;
                case NetTypeEnum.VerGradient:
                    resDescription += "VGr"; break;
                case NetTypeEnum.HorGradient:
                    resDescription += "HGr"; break;
                case NetTypeEnum.Chess:
                    resDescription += "Chess"; break;
                case NetTypeEnum.DottedColumn:
                    resDescription += "DotCol"; break;
                case NetTypeEnum.DottedRow:
                    resDescription += "DotRow"; break;
                case NetTypeEnum.HalfShiftedHTest:
                    resDescription += "HSHTst"; break;
                case NetTypeEnum.HTest:
                    resDescription += "HTst"; break;
                case NetTypeEnum.Rain:
                    resDescription += "Rain"; break;
                case NetTypeEnum.RingClosed:
                    resDescription += "RngCl"; break;
                case NetTypeEnum.RingHollow:
                    resDescription += "RngHl"; break;
                case NetTypeEnum.ShiftedHTest:
                    resDescription += "SHTst"; break;
                case NetTypeEnum.StripColumn:
                    resDescription += "StrCol"; break;
                case NetTypeEnum.StripRow:
                    resDescription += "StrRow"; break;
            }

            resDescription += " {" + Width.ToString() + "x" + Height.ToString();
            if (NetType == NetTypeEnum.Random)
                resDescription += "; " + Probability0.ToString() + "}";
            else if (NetType == NetTypeEnum.VerGradient || NetType == NetTypeEnum.HorGradient)
                resDescription += "; " + Probability0.ToString() + "; " + Probability1.ToString() + "}";
            else
                resDescription += "}";

            return resDescription;
        }

        public int Count 
        {
            get
            {
                if (NetType == NetTypeEnum.Random || NetType == NetTypeEnum.VerGradient || NetType == NetTypeEnum.HorGradient)
                    return m_count;
                else
                    return 1;
            }

            set
            {
                if (NetType == NetTypeEnum.Random || NetType == NetTypeEnum.VerGradient || NetType == NetTypeEnum.HorGradient)
                    m_count = value;
                else
                    throw new ApplicationException("Internal error");
            }
        }

        public double Probability0
        {
            get
            {
                if (NetType == NetTypeEnum.Random || NetType == NetTypeEnum.VerGradient || NetType == NetTypeEnum.HorGradient)
                    return m_probability0;
                else
                    throw new ApplicationException("Internal error");
            }

            set
            {
                if (NetType == NetTypeEnum.Random || NetType == NetTypeEnum.VerGradient || NetType == NetTypeEnum.HorGradient)
                    m_probability0 = value;
                else
                    throw new ApplicationException("Internal error");
            }
        }        
        public double Probability1
        {
            get
            {
                if (NetType == NetTypeEnum.VerGradient || NetType == NetTypeEnum.HorGradient)
                    return m_probability1;
                else
                    throw new ApplicationException("Internal error");
            }

            set
            {
                if (NetType == NetTypeEnum.VerGradient || NetType == NetTypeEnum.HorGradient)
                    m_probability1 = value;
                else
                    throw new ApplicationException("Internal error");
            }
        }
        public NetPackSettings()
        {
            NetType = NetTypeEnum.Random;
            Width = 50;
            Height = 50;
            m_count = 10;
            m_probability0 = 0.5;
            m_probability1 = 0.5;
        }

        public NetPackSettings(NetPackSettings instance)
        {
            NetType = instance.NetType;
            Width = instance.Width;
            Height = instance.Height;
            m_count = instance.m_count;
            m_probability0 = instance.m_probability0;
            m_probability1 = instance.m_probability1;
        }

        public NetParams getNetParams()
        {
            if (NetType == NetTypeEnum.Random)
                return new RandomNetParams(Width, Height, Probability0);
            else if (NetType == NetTypeEnum.VerGradient)
                return new GradientNetParams(true, Width, Height, Probability0, Probability1);
            else if (NetType == NetTypeEnum.HorGradient)
                return new GradientNetParams(false, Width, Height, Probability0, Probability1);
            else
                return new NetParams(NetType, Width, Height);
        }

        private int m_count = 1;
        private double m_probability0 = 0.0;
        private double m_probability1 = 0.0;
    }

    [Serializable]
    public sealed class NetRangeSettings
    {
        public int NetWidth { get; set; }
        public int NetHeight { get; set; }

        public RangeTypeEnum Type { get; set; }

        public int PackExperiments { 
            get
            {
                if (Type != RangeTypeEnum.CustomRange)
                    return m_packExperiments;
                else
                    throw new ApplicationException("Internal error.");
            }

            set
            {
                if (Type != RangeTypeEnum.CustomRange)
                    m_packExperiments = value;
                else
                    throw new ApplicationException("Internal error.");
            }
        }

        public List<NetPackSettings> PackSettingsList
        {
            get
            {
                if (Type == RangeTypeEnum.CustomRange)
                    return m_packSettingsList;
                else
                    throw new ApplicationException("Internal error.");
            }

            set
            {
                if (Type == RangeTypeEnum.CustomRange)
                    m_packSettingsList = value;
                else
                    throw new ApplicationException("Internal error.");
            }
        }

        public List<double> ProbabilityList
        {
            get
            {
                if (Type != RangeTypeEnum.CustomRange)
                    return m_probabilityList;
                else
                    throw new ApplicationException("Internal error.");
            }

            set
            {
                if (Type != RangeTypeEnum.CustomRange)
                    m_probabilityList = value;
                else
                    throw new ApplicationException("Internal error.");
            }
        }

        public double GradProbability
        {
            get
            {
                if (Type == RangeTypeEnum.HorGradientRange || Type == RangeTypeEnum.VerGradientRange)
                    return m_gradProbability;
                else
                    throw new ApplicationException("Internal error.");
            }

            set
            {
                if (Type == RangeTypeEnum.HorGradientRange || Type == RangeTypeEnum.VerGradientRange)
                    m_gradProbability = value;
                else
                    throw new ApplicationException("Internal error.");
            }
        }

        public NetRangeSettings()
        {
            NetWidth = 50;
            NetHeight = 50;
            m_packExperiments = 10;
            m_gradProbability = 0.5;
            Type = RangeTypeEnum.RandomRange;
        }

        public NetPackSettings getPackSettings(int packNum)
        {
            if (Type == RangeTypeEnum.CustomRange)
                return PackSettingsList[packNum];

            NetTypeEnum netType = new NetTypeEnum();
            if (Type == RangeTypeEnum.RandomRange)
                netType = NetTypeEnum.Random;
            else if (Type == RangeTypeEnum.HorGradientRange)
                netType = NetTypeEnum.HorGradient;
            else if (Type == RangeTypeEnum.VerGradientRange)
                netType = NetTypeEnum.VerGradient;

            NetPackSettings settings = new NetPackSettings();

            settings.NetType = netType;
            settings.Width = NetWidth;
            settings.Height = NetHeight;
            settings.Probability0 = m_probabilityList[packNum];
            settings.Count = m_packExperiments;

            if (Type != RangeTypeEnum.RandomRange)
                settings.Probability1 = m_gradProbability;

            return settings;
        }

        protected int m_packExperiments;
        protected List<double> m_probabilityList = new List<double>();
        protected List<NetPackSettings> m_packSettingsList = new List<NetPackSettings>();
        protected double m_gradProbability;
    }

    [Serializable]
    public sealed class AlgorithmsSettings
    {
        public bool ProcessBolt { get; set; }
        public System.Drawing.Color BoltRouteColor { get; set; }
        public System.Drawing.Color BoltAddedCellColor { get; set; }
        public EdgeWeightSettings BoltSettings { get; set; }

        public AlgorithmsSettings()
        {
            BoltSettings = new EdgeWeightSettings();
            ProcessBolt = true;
        }

        public void Copy(AlgorithmsSettings instance)
        {
            ProcessBolt = instance.ProcessBolt;
            BoltRouteColor = instance.BoltRouteColor;
            BoltAddedCellColor = instance.BoltAddedCellColor;
            BoltSettings = instance.BoltSettings;
        }
    }

    [Serializable]
    public sealed class ProjectSettings
    {

        public AlgorithmsSettings getAlgorithms()
        {
            return m_algorithms;
        }

        protected AlgorithmsSettings m_algorithms = new AlgorithmsSettings();
    }
}
